/* Redis CLI (command line interface)
 *
 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Redis nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "fmacros.h"
#include "version.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>

#include <hiredis.h>
#include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
#include "dict.h"
#include "adlist.h"
#include "zmalloc.h"
#include "linenoise.h"
#include "help.h"
#include "anet.h"
#include "ae.h"

#define REDIS_CLI_KEEPALIVE_INTERVAL 15   /* seconds */
#define REDIS_CLI_DEFAULT_PIPE_TIMEOUT 30 /* seconds */
#define CLUSTER_MANAGER_MIGRATE_TIMEOUT 60000
#define CLUSTER_MANAGER_MIGRATE_PIPELINE 10
#define CLUSTER_MANAGER_REBALANCE_THRESHOLD 2

#define OUTPUT_STANDARD 0
#define OUTPUT_RAW 1
#define OUTPUT_CSV 2
#define REDIS_CLI_AUTH_ENV "REDISCLI_AUTH"

/* Cluster Manager Command Info */
typedef struct clusterManagerCommand
{
    char *name;
    int argc;
    char **argv;
    int flags;
    int replicas;
    char *from;
    char *to;
    char **weight;
    int weight_argc;
    char *master_id;
    int slots;
    int timeout;
    int pipeline;
    float threshold;
} clusterManagerCommand;

static struct config
{
    char *hostip;
    int hostport;
    char *hostsocket;
    long repeat;
    long interval;
    int dbnum;
    int interactive;
    int shutdown;
    int monitor_mode;
    int pubsub_mode;
    int latency_mode;
    int latency_dist_mode;
    int latency_history;
    int lru_test_mode;
    long long lru_test_sample_size;
    int cluster_mode;
    int cluster_reissue_command;
    int slave_mode;
    int pipe_mode;
    int pipe_timeout;
    int getrdb_mode;
    int stat_mode;
    int scan_mode;
    int intrinsic_latency_mode;
    int intrinsic_latency_duration;
    char *pattern;
    char *rdb_filename;
    int bigkeys;
    int hotkeys;
    int stdinarg; /* get last arg from stdin. (-x option) */
    char *auth;
    int output; /* output mode, see OUTPUT_* defines */
    sds mb_delim;
    char prompt[128];
    char *eval;
    int eval_ldb;
    int eval_ldb_sync;      /* Ask for synchronous mode of the Lua debugger. */
    int eval_ldb_end;       /* Lua debugging session ended. */
    int enable_ldb_on_eval; /* Handle manual SCRIPT DEBUG + EVAL commands. */
    int last_cmd_type;
    int verbose;
    clusterManagerCommand cluster_manager_command;
    int no_auth_warning;
} config;

/* User preferences. */
static struct pref
{
    int hints;
} pref;

/* The actual palette in use. */
int *spectrum_palette;
int spectrum_palette_size;

/* --latency-dist palettes. */
int spectrum_palette_color_size = 19;
int spectrum_palette_color[] = {0, 233, 234, 235, 237, 239, 241, 243, 245, 247, 144, 143, 142, 184, 226, 214, 208, 202, 196};

static redisContext *context;

/*------------------------------------------------------------------------------
 * User interface
 *--------------------------------------------------------------------------- */

// static int parseOptions(int argc, char **argv)
// {
//     int i;

//     for (i = 1; i < argc; i++)
//     {
//         int lastarg = i == argc - 1;

//         if (!strcmp(argv[i], "-h") && !lastarg)
//         {
//             sdsfree(config.hostip);
//             config.hostip = sdsnew(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "-h") && lastarg)
//         {
//             usage();
//         }
//         else if (!strcmp(argv[i], "--help"))
//         {
//             usage();
//         }
//         else if (!strcmp(argv[i], "-x"))
//         {
//             config.stdinarg = 1;
//         }
//         else if (!strcmp(argv[i], "-p") && !lastarg)
//         {
//             config.hostport = atoi(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "-s") && !lastarg)
//         {
//             config.hostsocket = argv[++i];
//         }
//         else if (!strcmp(argv[i], "-r") && !lastarg)
//         {
//             config.repeat = strtoll(argv[++i], NULL, 10);
//         }
//         else if (!strcmp(argv[i], "-i") && !lastarg)
//         {
//             double seconds = atof(argv[++i]);
//             config.interval = seconds * 1000000;
//         }
//         else if (!strcmp(argv[i], "-n") && !lastarg)
//         {
//             config.dbnum = atoi(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--no-auth-warning"))
//         {
//             config.no_auth_warning = 1;
//         }
//         else if (!strcmp(argv[i], "-a") && !lastarg)
//         {
//             config.auth = argv[++i];
//         }
//         else if (!strcmp(argv[i], "-u") && !lastarg)
//         {
//             parseRedisUri(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--raw"))
//         {
//             config.output = OUTPUT_RAW;
//         }
//         else if (!strcmp(argv[i], "--no-raw"))
//         {
//             config.output = OUTPUT_STANDARD;
//         }
//         else if (!strcmp(argv[i], "--csv"))
//         {
//             config.output = OUTPUT_CSV;
//         }
//         else if (!strcmp(argv[i], "--latency"))
//         {
//             config.latency_mode = 1;
//         }
//         else if (!strcmp(argv[i], "--latency-dist"))
//         {
//             config.latency_dist_mode = 1;
//         }
//         else if (!strcmp(argv[i], "--mono"))
//         {
//             spectrum_palette = spectrum_palette_mono;
//             spectrum_palette_size = spectrum_palette_mono_size;
//         }
//         else if (!strcmp(argv[i], "--latency-history"))
//         {
//             config.latency_mode = 1;
//             config.latency_history = 1;
//         }
//         else if (!strcmp(argv[i], "--lru-test") && !lastarg)
//         {
//             config.lru_test_mode = 1;
//             config.lru_test_sample_size = strtoll(argv[++i], NULL, 10);
//         }
//         else if (!strcmp(argv[i], "--slave"))
//         {
//             config.slave_mode = 1;
//         }
//         else if (!strcmp(argv[i], "--replica"))
//         {
//             config.slave_mode = 1;
//         }
//         else if (!strcmp(argv[i], "--stat"))
//         {
//             config.stat_mode = 1;
//         }
//         else if (!strcmp(argv[i], "--scan"))
//         {
//             config.scan_mode = 1;
//         }
//         else if (!strcmp(argv[i], "--pattern") && !lastarg)
//         {
//             config.pattern = argv[++i];
//         }
//         else if (!strcmp(argv[i], "--intrinsic-latency") && !lastarg)
//         {
//             config.intrinsic_latency_mode = 1;
//             config.intrinsic_latency_duration = atoi(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--rdb") && !lastarg)
//         {
//             config.getrdb_mode = 1;
//             config.rdb_filename = argv[++i];
//         }
//         else if (!strcmp(argv[i], "--pipe"))
//         {
//             config.pipe_mode = 1;
//         }
//         else if (!strcmp(argv[i], "--pipe-timeout") && !lastarg)
//         {
//             config.pipe_timeout = atoi(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--bigkeys"))
//         {
//             config.bigkeys = 1;
//         }
//         else if (!strcmp(argv[i], "--hotkeys"))
//         {
//             config.hotkeys = 1;
//         }
//         else if (!strcmp(argv[i], "--eval") && !lastarg)
//         {
//             config.eval = argv[++i];
//         }
//         else if (!strcmp(argv[i], "--ldb"))
//         {
//             config.eval_ldb = 1;
//             config.output = OUTPUT_RAW;
//         }
//         else if (!strcmp(argv[i], "--ldb-sync-mode"))
//         {
//             config.eval_ldb = 1;
//             config.eval_ldb_sync = 1;
//             config.output = OUTPUT_RAW;
//         }
//         else if (!strcmp(argv[i], "-c"))
//         {
//             config.cluster_mode = 1;
//         }
//         else if (!strcmp(argv[i], "-d") && !lastarg)
//         {
//             sdsfree(config.mb_delim);
//             config.mb_delim = sdsnew(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--verbose"))
//         {
//             config.verbose = 1;
//         }
//         else if (!strcmp(argv[i], "--cluster") && !lastarg)
//         {
//             if (CLUSTER_MANAGER_MODE())
//                 usage();
//             char *cmd = argv[++i];
//             int j = i;
//             while (j < argc && argv[j][0] != '-')
//                 j++;
//             if (j > i)
//                 j--;
//             createClusterManagerCommand(cmd, j - i, argv + i + 1);
//             i = j;
//         }
//         else if (!strcmp(argv[i], "--cluster") && lastarg)
//         {
//             usage();
//         }
//         else if (!strcmp(argv[i], "--cluster-replicas") && !lastarg)
//         {
//             config.cluster_manager_command.replicas = atoi(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--cluster-master-id") && !lastarg)
//         {
//             config.cluster_manager_command.master_id = argv[++i];
//         }
//         else if (!strcmp(argv[i], "--cluster-from") && !lastarg)
//         {
//             config.cluster_manager_command.from = argv[++i];
//         }
//         else if (!strcmp(argv[i], "--cluster-to") && !lastarg)
//         {
//             config.cluster_manager_command.to = argv[++i];
//         }
//         else if (!strcmp(argv[i], "--cluster-weight") && !lastarg)
//         {
//             if (config.cluster_manager_command.weight != NULL)
//             {
//                 fprintf(stderr, "WARNING: you cannot use --cluster-weight "
//                                 "more than once.\n"
//                                 "You can set more weights by adding them "
//                                 "as a space-separated list, ie:\n"
//                                 "--cluster-weight n1=w n2=w\n");
//                 exit(1);
//             }
//             int widx = i + 1;
//             char **weight = argv + widx;
//             int wargc = 0;
//             for (; widx < argc; widx++)
//             {
//                 if (strstr(argv[widx], "--") == argv[widx])
//                     break;
//                 if (strchr(argv[widx], '=') == NULL)
//                     break;
//                 wargc++;
//             }
//             if (wargc > 0)
//             {
//                 config.cluster_manager_command.weight = weight;
//                 config.cluster_manager_command.weight_argc = wargc;
//                 i += wargc;
//             }
//         }
//         else if (!strcmp(argv[i], "--cluster-slots") && !lastarg)
//         {
//             config.cluster_manager_command.slots = atoi(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--cluster-timeout") && !lastarg)
//         {
//             config.cluster_manager_command.timeout = atoi(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--cluster-pipeline") && !lastarg)
//         {
//             config.cluster_manager_command.pipeline = atoi(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--cluster-threshold") && !lastarg)
//         {
//             config.cluster_manager_command.threshold = atof(argv[++i]);
//         }
//         else if (!strcmp(argv[i], "--cluster-yes"))
//         {
//             config.cluster_manager_command.flags |=
//                 CLUSTER_MANAGER_CMD_FLAG_YES;
//         }
//         else if (!strcmp(argv[i], "--cluster-simulate"))
//         {
//             config.cluster_manager_command.flags |=
//                 CLUSTER_MANAGER_CMD_FLAG_SIMULATE;
//         }
//         else if (!strcmp(argv[i], "--cluster-replace"))
//         {
//             config.cluster_manager_command.flags |=
//                 CLUSTER_MANAGER_CMD_FLAG_REPLACE;
//         }
//         else if (!strcmp(argv[i], "--cluster-copy"))
//         {
//             config.cluster_manager_command.flags |=
//                 CLUSTER_MANAGER_CMD_FLAG_COPY;
//         }
//         else if (!strcmp(argv[i], "--cluster-slave"))
//         {
//             config.cluster_manager_command.flags |=
//                 CLUSTER_MANAGER_CMD_FLAG_SLAVE;
//         }
//         else if (!strcmp(argv[i], "--cluster-use-empty-masters"))
//         {
//             config.cluster_manager_command.flags |=
//                 CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER;
//         }
//         else if (!strcmp(argv[i], "--cluster-search-multiple-owners"))
//         {
//             config.cluster_manager_command.flags |=
//                 CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;
//         }
//         else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version"))
//         {
//             sds version = cliVersion();
//             printf("redis-cli %s\n", version);
//             sdsfree(version);
//             exit(0);
//         }
//         else if (CLUSTER_MANAGER_MODE() && argv[i][0] != '-')
//         {
//             if (config.cluster_manager_command.argc == 0)
//             {
//                 int j = i + 1;
//                 while (j < argc && argv[j][0] != '-')
//                     j++;
//                 int cmd_argc = j - i;
//                 config.cluster_manager_command.argc = cmd_argc;
//                 config.cluster_manager_command.argv = argv + i;
//                 if (cmd_argc > 1)
//                     i = j - 1;
//             }
//         }
//         else
//         {
//             if (argv[i][0] == '-')
//             {
//                 fprintf(stderr,
//                         "Unrecognized option or bad number of args for: '%s'\n",
//                         argv[i]);
//                 exit(1);
//             }
//             else
//             {
//                 /* Likely the command name, stop here. */
//                 break;
//             }
//         }
//     }

//     /* --ldb requires --eval. */
//     if (config.eval_ldb && config.eval == NULL)
//     {
//         fprintf(stderr, "Options --ldb and --ldb-sync-mode require --eval.\n");
//         fprintf(stderr, "Try %s --help for more information.\n", argv[0]);
//         exit(1);
//     }

//     if (!config.no_auth_warning && config.auth != NULL)
//     {
//         fputs("Warning: Using a password with '-a' or '-u' option on the command"
//               " line interface may not be safe.\n",
//               stderr);
//     }

//     return i;
// }

static void parseEnv()
{
    /* Set auth from env, but do not overwrite CLI arguments if passed */
    char *auth = getenv(REDIS_CLI_AUTH_ENV);
    if (auth != NULL && config.auth == NULL)
    {
        config.auth = auth;
    }
}

/*------------------------------------------------------------------------------
 * Networking / parsing
 *--------------------------------------------------------------------------- */

/* Send AUTH command to the server */
static int cliAuth(void)
{
    redisReply *reply;
    if (config.auth == NULL)
        return REDIS_OK;

    reply = redisCommand(context, "AUTH %s", config.auth);
    if (reply != NULL)
    {
        freeReplyObject(reply);
        return REDIS_OK;
    }
    return REDIS_ERR;
}

/* Send SELECT dbnum to the server */
static int cliSelect(void)
{
    redisReply *reply;
    if (config.dbnum == 0)
        return REDIS_OK;

    reply = redisCommand(context, "SELECT %d", config.dbnum);
    if (reply != NULL)
    {
        int result = REDIS_OK;
        if (reply->type == REDIS_REPLY_ERROR)
            result = REDIS_ERR;
        freeReplyObject(reply);
        return result;
    }
    return REDIS_ERR;
}

/* Connect to the server. It is possible to pass certain flags to the function:
 *      CC_FORCE: The connection is performed even if there is already
 *                a connected socket.
 *      CC_QUIET: Don't print errors if connection fails. */
static int cliConnect(int flags)
{
    flags = 1;
    //如果连接不存在 或者 强制连接
    if (context == NULL)
    {
        if (context != NULL)
        {
            redisFree(context);
        }

        if (config.hostsocket == NULL)
        {
            context = redisConnect(config.hostip, config.hostport);
        }
        else
        {
            context = redisConnectUnix(config.hostsocket);
        }

        if (context->err)
        {
            redisFree(context);
            context = NULL;
            return REDIS_ERR;
        }

        /* Set aggressive KEEP_ALIVE socket option in the Redis context socket
         * in order to prevent timeouts caused by the execution of long
         * commands. At the same time this improves the detection of real
         * errors. */
        anetKeepAlive(NULL, context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);

        /* Do AUTH and select the right DB. */
        if (cliAuth() != REDIS_OK)
            return REDIS_ERR;
        if (cliSelect() != REDIS_OK)
            return REDIS_ERR;
    }
    return REDIS_OK;
}

static sds readArgFromStdin(void)
{
    char buf[1024];
    sds arg = sdsempty();

    while (1)
    {
        int nread = read(fileno(stdin), buf, 1024);

        if (nread == 0)
            break;
        else if (nread == -1)
        {
            perror("Reading from standard input");
            exit(1);
        }
        arg = sdscatlen(arg, buf, nread);
    }
    return arg;
}

static int noninteractive(int argc, char **argv)
{
    int retval = 0;
    if (config.stdinarg)
    {
        argv = zrealloc(argv, (argc + 1) * sizeof(char *));
        argv[argc] = readArgFromStdin();
        //retval = issueCommand(argc + 1, argv);
    }
    else
    {
        //retval = issueCommand(argc, argv);
    }
    return retval;
}

/* Turn the plain C strings into Sds strings */
static char **convertToSds(int count, char** args) {
  int j;
  char **sds = zmalloc(sizeof(char*)*count);

  for(j = 0; j < count; j++)
    sds[j] = sdsnew(args[j]);

  return sds;
}


/*------------------------------------------------------------------------------
 * Program main()
 *--------------------------------------------------------------------------- */

int main(int argc, char **argv) {
    printf("redis-cli %s\n", "12345");
    //int firstarg;
    //SDS（Simple Dynamic Strings）是一个C语言字符串库，设计中增加了从堆上分配内存的字符串，来扩充有限的libc字符处理的功能
    config.hostip = sdsnew("127.0.0.1");
    config.hostport = 6379;
    config.hostsocket = NULL;
    config.repeat = 1;
    config.interval = 0;
    config.dbnum = 0;
    config.interactive = 0;
    config.shutdown = 0;
    config.monitor_mode = 0;
    config.pubsub_mode = 0;
    config.latency_mode = 0;
    config.latency_dist_mode = 0;
    config.latency_history = 0;
    config.lru_test_mode = 0;
    config.lru_test_sample_size = 0;
    config.cluster_mode = 0;
    config.slave_mode = 0;
    config.getrdb_mode = 0;
    config.stat_mode = 0;
    config.scan_mode = 0;
    config.intrinsic_latency_mode = 0;
    config.pattern = NULL;
    config.rdb_filename = NULL;
    config.pipe_mode = 0;
    config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT;
    config.bigkeys = 0;
    config.hotkeys = 0;
    config.stdinarg = 0;
    config.auth = NULL;
    config.eval = NULL;
    config.eval_ldb = 0;
    config.eval_ldb_end = 0;
    config.eval_ldb_sync = 0;
    config.enable_ldb_on_eval = 0;
    config.last_cmd_type = -1;
    config.verbose = 0;
    config.no_auth_warning = 0;
    config.cluster_manager_command.name = NULL;
    config.cluster_manager_command.argc = 0;
    config.cluster_manager_command.argv = NULL;
    config.cluster_manager_command.flags = 0;
    config.cluster_manager_command.replicas = 0;
    config.cluster_manager_command.from = NULL;
    config.cluster_manager_command.to = NULL;
    config.cluster_manager_command.weight = NULL;
    config.cluster_manager_command.weight_argc = 0;
    config.cluster_manager_command.slots = 0;
    config.cluster_manager_command.timeout = CLUSTER_MANAGER_MIGRATE_TIMEOUT;
    config.cluster_manager_command.pipeline = CLUSTER_MANAGER_MIGRATE_PIPELINE;
    config.cluster_manager_command.threshold = CLUSTER_MANAGER_REBALANCE_THRESHOLD;
    pref.hints = 1;

    spectrum_palette = spectrum_palette_color;
    spectrum_palette_size = spectrum_palette_color_size;

    if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL))
        config.output = OUTPUT_RAW;
    else
        config.output = OUTPUT_STANDARD;
    config.mb_delim = sdsnew("\n");
    //解析参数
    //firstarg = parseOptions(argc,argv);
    //argc -= firstarg;
    //argv += firstarg;

    parseEnv();

    printf("redis-cli %s\n", "12345");
    /* Otherwise, we have some arguments to execute */
    if (cliConnect(0) != REDIS_OK) exit(1);
    
    
    if (config.eval) {
        //return evalMode(argc,argv);
    } else {
        return noninteractive(argc,convertToSds(argc,argv));
    }
}
